#include "Config.hpp"
#include "../inc/GxIAPI.h"
#include <cstring>
#include <nlohmann/json.hpp>
#include <stdexcept>

#define CHECK_API_SUCCESS(func)                                                \
  if (func != GX_STATUS_SUCCESS)                                               \
    return false;

enum DahengConfigType { D_INT = 0, D_FLOAT, D_STRING, D_ENUM, D_BOOL };
struct DahengConfigInf {
  int64_t setitem;
  const char *describe;
  DahengConfigType type;
  bool readonly;
};

static const DahengConfigInf DahengConfigList[] = {
    {GX_INT_HEIGHT_MAX, "最大高度", DahengConfigType::D_INT, true},
    {GX_INT_WIDTH_MAX, "最大宽度", DahengConfigType::D_INT, true},
    {GX_INT_HEIGHT, "高度", DahengConfigType::D_INT, false},
    {GX_INT_WIDTH, "宽度", DahengConfigType::D_INT, false},
    {GX_INT_OFFSET_X, "X偏移量", DahengConfigType::D_INT, false},
    {GX_INT_OFFSET_Y, "Y偏移量", DahengConfigType::D_INT, false},
    {GX_FLOAT_EXPOSURE_TIME, "曝光时间", DahengConfigType::D_FLOAT, false},
    {GX_STRING_DEVICE_SERIAL_NUMBER, "序列号", DahengConfigType::D_STRING,
     true},
    {GX_ENUM_ACQUISITION_FRAME_RATE_MODE, "帧率限制使能",
     DahengConfigType::D_ENUM, false},
    {GX_FLOAT_ACQUISITION_FRAME_RATE, "帧率限制", DahengConfigType::D_FLOAT,
     false},
    {GX_ENUM_TRIGGER_MODE, "触发模式", DahengConfigType::D_ENUM, false},
};

DahengConfig::DahengConfig(GX_DEV_HANDLE obj) : handle(obj) {}

static long GetConfigListSize() {
  return sizeof(DahengConfigList) / sizeof(DahengConfigInf);
}

std::vector<BaseConfig::item> DahengConfig::ItemList() {
  std::vector<BaseConfig::item> res(GetConfigListSize());
  for (long i = 0; i < GetConfigListSize(); i++) {
    res[i].id = i;
    res[i].type = static_cast<BaseConfig::item_type>(DahengConfigList[i].type);
    res[i].readonly = DahengConfigList[i].readonly;
    res[i].describe = [this](const DahengConfigInf &obj) -> std::string {
      nlohmann::json res;
      res["name"] = obj.describe;
      switch (obj.type) {
      case DahengConfigType::D_INT: {
        auto [max, min] = GetIntRange(obj.setitem);
        res["range"] = {
            {"max", max},
            {"min", min},
        };
      } break;
      case DahengConfigType::D_FLOAT: {
        auto [max, min] = GetDoubleRange(obj.setitem);
        res["range"] = {
            {"max", max},
            {"min", min},
        };
      } break;
      case DahengConfigType::D_ENUM: {
        res["obj"] = GetEnumRange(obj.setitem);
      } break;
      case DahengConfigType::D_STRING: {
        res["strlen"] = GetStrRange(obj.setitem);
      } break;
      case DahengConfigType::D_BOOL:
        break;
      }
      return res.dump();
    }(DahengConfigList[i]);
  }
  return res;
}

bool DahengConfig::Get(int id, std::string &value) {
  if (DahengConfigList[id].type != D_STRING)
    throw std::logic_error("DahengConfig GetString type error");
  size_t nsize = 0;
  CHECK_API_SUCCESS(
      GXGetStringMaxLength(handle, DahengConfigList[id].setitem, &nsize))
  char *cstr = new char[nsize];
  CHECK_API_SUCCESS(
      GXGetString(handle, DahengConfigList[id].setitem, cstr, &nsize));
  value = cstr;
  delete[] cstr;
  return true;
}

bool DahengConfig::Get(int id, double &value) {
  if (DahengConfigList[id].type != D_FLOAT)
    throw std::logic_error("DahengConfig GetDouble type error");
  CHECK_API_SUCCESS(GXGetFloat(handle, DahengConfigList[id].setitem, &value))
  return true;
}

bool DahengConfig::Get(int id, long &value) {
  if (DahengConfigList[id].type == D_INT) {
    CHECK_API_SUCCESS(GXGetInt(handle, DahengConfigList[id].setitem, &value))
  } else if (DahengConfigList[id].type == D_ENUM) {
    // 先获取选项
    uint32_t nEntryNums = 0;
    CHECK_API_SUCCESS(
        GXGetEnumEntryNums(handle, DahengConfigList[id].setitem, &nEntryNums))
    GX_ENUM_DESCRIPTION *ilist = new GX_ENUM_DESCRIPTION[nEntryNums];
    size_t nsize = nEntryNums * sizeof(GX_ENUM_DESCRIPTION);
    CHECK_API_SUCCESS(GXGetEnumDescription(handle, DahengConfigList[id].setitem,
                                           ilist, &nsize))

    // 然后获取值
    int64_t nval = 0;
    CHECK_API_SUCCESS(GXGetEnum(handle, DahengConfigList[id].setitem, &nval));

    // 映射过去
    long resv = 0;
    for (; resv < nEntryNums; resv++)
      if (nval == ilist[resv].nValue)
        break;

    // 传出
    value = resv;
    delete[] ilist;
  } else
    throw std::logic_error("DahengConfig GetLong type error");
  return true;
}

bool DahengConfig::Get(int id, bool &value) {
  if (DahengConfigList[id].type != D_BOOL)
    throw std::logic_error("DahengConfig GetBool type error");
  CHECK_API_SUCCESS(GXGetBool(handle, DahengConfigList[id].setitem, &value));
  return true;
}

bool DahengConfig::Set(int id, const std::string &value) {
  char *str = new char[value.size() + 1];
  value.copy(str, value.size());
  str[value.size()] = '\0';
  CHECK_API_SUCCESS(GXSetString(handle, DahengConfigList[id].setitem, str));
  delete[] str;
  return true;
}

bool DahengConfig::Set(int id, const long &value) {
  if (GXSetInt(handle, DahengConfigList[id].setitem, value) ==
      GX_STATUS_SUCCESS)
    return true;
  if (GXSetEnum(handle, DahengConfigList[id].setitem, value) ==
      GX_STATUS_SUCCESS)
    return true;
  return false;
}

bool DahengConfig::Set(int id, const double &value) {
  CHECK_API_SUCCESS(GXSetFloat(handle, DahengConfigList[id].setitem, value));
  return true;
}

bool DahengConfig::Set(int id, const bool &value) {
  CHECK_API_SUCCESS(GXSetBool(handle, DahengConfigList[id].setitem, value));
  return true;
}

std::pair<long, long> DahengConfig::GetIntRange(int64_t item) {
  GX_INT_RANGE range;
  if (GXGetIntRange(handle, item, &range) != GX_STATUS_SUCCESS)
    throw std::runtime_error("DahengConfig Can't get IntRange");
  return {range.nMax, range.nMin};
}

std::vector<std::string> DahengConfig::GetEnumRange(int64_t item) {
  uint32_t nEntryNums = 0;
  if (GXGetEnumEntryNums(handle, item, &nEntryNums))
    throw std::runtime_error("DahengConfig Can't get EntryNums");
  GX_ENUM_DESCRIPTION *listitem = new GX_ENUM_DESCRIPTION[nEntryNums];
  size_t nSize = nEntryNums * sizeof(GX_ENUM_DESCRIPTION);
  if (GXGetEnumDescription(handle, item, listitem, &nSize) != GX_STATUS_SUCCESS)
    throw std::runtime_error("DahengConfig Can't get EnumDescription");
  std::vector<std::string> res(nEntryNums);
  for (int i = 0; i < nEntryNums; i++)
    res[i] = listitem[i].szSymbolic;
  delete[] listitem;
  return res;
}

std::pair<double, double> DahengConfig::GetDoubleRange(int64_t item) {
  GX_FLOAT_RANGE range;
  if (GXGetFloatRange(handle, item, &range) != GX_STATUS_SUCCESS)
    throw std::runtime_error("DahengConfig Can't get FloatRange");
  return {range.dMax, range.dMin};
}

long DahengConfig::GetStrRange(int64_t item) {
  size_t size = 0;
  if (GXGetStringMaxLength(handle, item, &size) != GX_STATUS_SUCCESS)
    throw std::runtime_error("DahengConfig Can't get StringMaxLength");
  return size;
}

#undef CHECK_API_SUCCESS
